/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.inspur.edp.web.frontendproject.changedetect.compile.stepexecute;

import com.inspur.edp.lcm.metadata.api.entity.OperationEnum;
import com.inspur.edp.lcm.metadata.api.service.MetadataProjectService;
import com.inspur.edp.web.common.JITEngineConstants;
import com.inspur.edp.web.common.entity.TerminalType;
import com.inspur.edp.web.common.io.FileUtility;
import com.inspur.edp.web.common.logger.WebLogger;
import com.inspur.edp.web.common.utility.Base64Utility;
import com.inspur.edp.web.frontendproject.changedetect.ChangeDetectExecuteType;
import com.inspur.edp.web.frontendproject.changedetect.ExcludePathEntity;
import com.inspur.edp.web.frontendproject.changedetect.compile.AbstractCompileChangeDetectStepExecuteService;
import com.inspur.edp.web.frontendproject.changedetect.context.ChangeDetectContext;
import com.inspur.edp.web.frontendproject.changedetect.step.ChangeDetectStepExecuteResult;
import com.inspur.edp.web.frontendproject.changedetect.step.ChangeDetectStepExecuteService;
import com.inspur.edp.web.npmpackage.core.npminstall.PackageJsonPathGenerator;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @Title: FileChangeStepExecuteImpl
 * @Description: com.inspur.edp.web.frontendproject.changedetect.compile.stepexecute
 * @Author: Noah
 * @Version: V1.0
 * @Create: 2022/6/21 19:45
 */
public class FileChangeStepExecuteImpl extends AbstractCompileChangeDetectStepExecuteService implements ChangeDetectStepExecuteService {
    /**
     * 步骤执行器参数
     *
     * @param context 变更检测上下文参数
     * @return
     */
    @Override
    public ChangeDetectStepExecuteResult execute(ChangeDetectContext context) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Compile);

        String srcAppPath = this.getSrcAppPath(context);
        String srcDistRollup = FileUtility.combine(srcAppPath, JITEngineConstants.DistRollupPathName);
        if (!FileUtility.exists(srcDistRollup)) {
            executeResult.setPass(false);
            executeResult.setReason(this.generateReasonWithPrefix("dist-rollup目录不存在"));
            return executeResult;
        }

        // 仅在pc 表单类型下检测
        if (context.getTerminalType() == TerminalType.PC) {
            String srcOutTscPath = FileUtility.combine(srcAppPath, "out-tsc");
            if (!FileUtility.exists(srcOutTscPath)) {
                executeResult.setPass(false);
                executeResult.setReason(this.generateReasonWithPrefix("out-tsc目录不存在"));
                return executeResult;
            }

            // 由于移动表单采用的是生成创建node_modules链接  因此 编译时不进行验证
            String srcNodeModulesPath = FileUtility.combine(srcAppPath, "node_modules");
            if (!FileUtility.exists(srcNodeModulesPath)) {
                executeResult.setPass(false);
                executeResult.setReason(this.generateReasonWithPrefix("node_modules目录不存在"));
                return executeResult;
            }

        }

        MetadataProjectService projectService = this.getMetadataProjectService();
        List<ExcludePathEntity> excludePathEntityList = this.getNeedExcludePathList(context);
        // 检测源代码是否发生变更
        boolean fileChangedFlag = projectService.isFileChanged(srcAppPath, OperationEnum.WEB_GENERATE, (dir, name) -> {
            // 如果进行了排除 那么不进行目录检测
            boolean matchExcludePathFlag = excludePathEntityList.stream().anyMatch(t -> t.getParentDir() != null &&
                    t.getParentDirFile().equals(dir) &&
                    Objects.equals(t.getName(), name));
            return !matchExcludePathFlag;
        });
        if (fileChangedFlag) {
            executeResult.setPass(false);
            executeResult.setReason(this.generateReasonWithPrefix("ts源代码发生变更"));
            return executeResult;
        }

        // 执行npm版本检测
        String currentServerPath = FileUtility.getCurrentWorkPath(false);
        String serverPackageJsonPath = PackageJsonPathGenerator.generate(currentServerPath, false);
        if (FileUtility.exists(serverPackageJsonPath)) {
            try {
                String base64EncodeValue = Base64Utility.encode(FileUtility.readAsString(serverPackageJsonPath)).substring(0, 32);
                boolean packageJsonFileChanged = projectService.isFileChanged(context.getProjectPath(), serverPackageJsonPath, base64EncodeValue, OperationEnum.WEB_GENERATE);
                if (packageJsonFileChanged) {
                    executeResult.setUnPassReason("node_modules发生变更");
                }
            } catch (UnsupportedEncodingException e) {
                WebLogger.Instance.error(e);
            }
        }


        return executeResult;
    }

    /**
     * 针对元数据的变更回写动作
     *
     * @param context
     * @return
     */
    @Override
    public ChangeDetectStepExecuteResult updateChangeset(ChangeDetectContext context) {
        ChangeDetectStepExecuteResult executeResult = ChangeDetectStepExecuteResult.getPassResult(ChangeDetectExecuteType.Compile);

        MetadataProjectService projectService = getMetadataProjectService();
        String srcAppPath = this.getSrcAppPath(context);
        List<ExcludePathEntity> excludePathEntityList = this.getNeedExcludePathList(context);
        // 检测源代码是否发生变更
        projectService.updateFileChanges(srcAppPath, OperationEnum.WEB_GENERATE, (dir, name) -> {
            // 如果进行了排除 那么布进行目录检测
            boolean matchExcludePathFlag = excludePathEntityList.stream().anyMatch(t -> t.getParentDir() != null &&
                    t.getParentDirFile().equals(dir) &&
                    Objects.equals(t.getName(), name));
            return !matchExcludePathFlag;
        });

        // 更新node_modules依赖的package.json 文件
        String currentServerPath = FileUtility.getCurrentWorkPath(false);
        String serverPackageJsonPath = PackageJsonPathGenerator.generate(currentServerPath, false);
        if (FileUtility.exists(serverPackageJsonPath)) {
            try {
                String base64EncodeValue = Base64Utility.encode(FileUtility.readAsString(serverPackageJsonPath)).substring(0, 32);
                projectService.updateFileChanges(context.getProjectPath(), serverPackageJsonPath, base64EncodeValue, OperationEnum.WEB_GENERATE);
            } catch (UnsupportedEncodingException e) {
                WebLogger.Instance.error(e);
            }

        }

        return executeResult;
    }

    /**
     * 获取需要进行文件目录检测的路径列表
     *
     * @param context
     * @return
     */
    private List<ExcludePathEntity> getNeedCheckPathList(ChangeDetectContext context) {
        List<ExcludePathEntity> checkPathList = new ArrayList<>(10);

        String srcAppPath = this.getSrcAppPath(context);

        checkPathList.add(new ExcludePathEntity(srcAppPath, "projects"));
        checkPathList.add(new ExcludePathEntity(srcAppPath, "angular.json"));
        checkPathList.add(new ExcludePathEntity(srcAppPath, "package.json"));
        checkPathList.add(new ExcludePathEntity(srcAppPath, "tsconfig.json"));
        return checkPathList;
    }

    /**
     * 不检测文件变化
     *
     * @param context
     * @return
     */
    private List<ExcludePathEntity> getNeedExcludePathList(ChangeDetectContext context) {
        List<ExcludePathEntity> excludePathList = new ArrayList<>();
        // 排除dist-rollup的变更
        String srcAppPath = this.getSrcAppPath(context);
        ExcludePathEntity excludeDistRollupPathEntity = new ExcludePathEntity(srcAppPath, JITEngineConstants.DistRollupPathName);
        excludePathList.add(excludeDistRollupPathEntity);

        ExcludePathEntity excludeNodeModulesPathEntity = new ExcludePathEntity(srcAppPath, "node_modules");
        excludePathList.add(excludeNodeModulesPathEntity);


        ExcludePathEntity excludeOuttscPathEntity = new ExcludePathEntity(srcAppPath, "out-tsc");
        excludePathList.add(excludeOuttscPathEntity);

        return excludePathList;
    }
}
